package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 报警数据Service业务层处理
 * 
 * @author ghy
 * @date 2025-08-26
 */
@Service
@Slf4j
public class AlertDataServiceImpl extends ServiceImpl<AlertDataMapper, AlertData> implements IAlertDataService
{
    @Autowired
    private AlertDataMapper alertDataMapper;
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 查询报警数据
     * 
     * @param id 报警数据主键
     * @return 报警数据
     */
    @Override
    public AlertData selectAlertDataById(Long id)
    {
        return alertDataMapper.selectById(id);
    }

    /**
     * 查询报警数据列表
     * 
     * @param alertData 报警数据
     * @return 报警数据
     */
    @Override
    public List<AlertData> selectAlertDataList(AlertData alertData)
    {
        return alertDataMapper.selectAlertDataList(alertData);
    }

    /**
     * 新增报警数据
     * 
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int insertAlertData(AlertData alertData)
    {
        return alertDataMapper.insert(alertData);
    }

    /**
     * 修改报警数据
     * 
     * @param alertData 报警数据
     * @return 结果
     */
    @Override
    public int updateAlertData(AlertData alertData)
    {
        return alertDataMapper.updateById(alertData);
    }

    /**
     * 批量删除报警数据
     * 
     * @param ids 需要删除的报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataByIds(Long[] ids)
    {
        return alertDataMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警数据信息
     * 
     * @param id 报警数据主键
     * @return 结果
     */
    @Override
    public int deleteAlertDataById(Long id)
    {
        return alertDataMapper.deleteById(id);
    }

    /**
     * 过滤设备上报数据是否满足报警规则，保存到数据库
     */
    @Override
    public void alertFilter() {
        // 1. 统计已启用规则的数量，如果没有，直接结束
        Long count = alertRuleMapper.selectCount(new LambdaQueryWrapper<AlertRule>().eq(AlertRule::getStatus, 1));
        if (count <= 0) {
            return;
        }
        // 2. 从Redis中获取所有设备上报的数据
        List<List<DeviceData>> values = redisTemplate.opsForHash().values(Constants.CACHE_IOT_DEVICE_LAST_DATA);
        // 3. 没有数据，直接结束
        if(CollUtil.isEmpty(values)) {
            return;
        }
        // 4. 开始遍历数据，处理每一条数据，否满足报警规则
        List<DeviceData> allDeviceData = new ArrayList<>();
        values.forEach(allDeviceData::addAll);
        allDeviceData.forEach(this::dataAlertFilter);
    }

    /**
     * 数据报警过滤
     * @param data
     */
    private void dataAlertFilter(DeviceData data) {
        // 1.如果当前上报的数据超过了一分钟，无效数据，直接结束
        if (data.getAlarmTime().plusMinutes(1).isBefore(LocalDateTime.now())) {
            return;
        }
        // 2.获取产品下设备的对应的物模型规则
        List<AlertRule> alertRules = alertRuleMapper.selectList(
                new LambdaQueryWrapper<AlertRule>()
                        .eq(AlertRule::getProductKey, data.getProductKey())
                        .in(AlertRule::getIotId, -1, data.getIotId())
                        .eq(AlertRule::getFunctionId, data.getFunctionId())
                        .eq(AlertRule::getStatus, 1));
        // 3.判断规则是否为空，为空，直接结束
        if(CollUtil.isEmpty(alertRules)) {
            return;
        }
        // 4.遍历每一条规则
        alertRules.forEach(rule -> dataIsMatchRule(rule, data));
    }

    /**
     * 判断数据是否满足报警规则
     * @param rule
     * @param data
     */
    private void dataIsMatchRule(AlertRule rule, DeviceData data) {
        String durationKey = Constants.ALERT_TRIGGER_COUNT_PREFIX + data.getProductKey() + "_" + data.getIotId() + "_" + data.getFunctionId() + "_" + rule.getId();
        String silentKey = Constants.ALERT_SILENT_PREFIX + data.getProductKey() + "_" + data.getIotId() + "_" + data.getFunctionId() + "_" + rule.getId() ;

        // 1.判断是否在生效时间内，不在，直接结束
        if (!alarmTimeIsMatch(rule, data)) {
            return;
        }
        // 2.检查上报数据是否达到阈值，未到达，删除报警数(持续周期)，直接结束
        if(!dataValueIsMatch(rule, data)){
            //删除报警数(持续周期)
            redisTemplate.delete(durationKey);
            return;
        }
        // 3.判断是否有沉默周期，有，直接结束
        String silenceCycle = (String) redisTemplate.opsForValue().get(silentKey);
        if(StringUtils.isNotEmpty(silenceCycle)) {
            return;
        }
        // 4.查询Redis中的报警数，如果没有，则设置成1，有，则加1
        Integer count = (Integer) redisTemplate.opsForValue().get(durationKey);
        if(Objects.isNull(count)) {
            count = 1;
        } else {
            count ++;
        }
        // 5.判断报警数是否达到持续周期，未达到，将报警数重新更新回Redis，直接结束
        if(ObjUtil.notEqual(count, rule.getDuration())) {
            redisTemplate.opsForValue().set(durationKey, count);
            return;
        }
        // 6.Redis删除报警数，增加沉默周期
        redisTemplate.delete(durationKey);
        redisTemplate.opsForValue().set(silentKey, "1", rule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        // 7.批量保存报警数据
        batchInsertAlertData(rule, data);
    }

    /**
     * 批量保存报警数据
     * @param data
     */
    private void batchInsertAlertData(AlertRule rule, DeviceData data) {
        // 查询报警数据要通知的用户列表
        List<Long> adminIds = sysUserRoleMapper.selectUserIdByRoleName("超级管理员");
        List<Long> otherUserIds = null;
        if(rule.getAlertDataType() == 0) {
            if (data.getLocationType() == 0) {
                //老人
                otherUserIds = deviceMapper.selectNursingIdsByIotIdWithElder(data.getIotId());
            } else {
                //床位
                otherUserIds = deviceMapper.selectNursingIdsByIotIdWithBed(data.getIotId());
            }
        } else {
            otherUserIds = sysUserRoleMapper.selectUserIdByRoleName("维修工");
        }
        Collection<Long> userIds = CollUtil.addAll(adminIds, otherUserIds);

        List<AlertData> alertDataList = new ArrayList<>();
        userIds.forEach(userId -> {
            AlertData alertData = BeanUtil.toBean(data, AlertData.class);
            alertData.setId(null);
            alertData.setAlertRuleId(rule.getId());
            String reason = StringUtils.format("报警原因:{}{}{}, 持续{}个周期", rule.getFunctionName(), rule.getOperator(), rule.getValue(), rule.getDuration());
            alertData.setAlertReason(reason);
            alertData.setType(rule.getAlertDataType());
            alertData.setStatus(0);
            alertData.setUserId(userId);
            alertDataList.add(alertData);
        });
        this.saveBatch(alertDataList);

        // 通过websocket推送消息给用户
        webSocketNotify(alertDataList.get(0), rule, userIds);
    }

    /**
     * 通知报警数据到具体的人员
     * @param alertData
     * @param userIds
     */
    private void webSocketNotify(AlertData alertData, AlertRule rule, Collection<Long> userIds) {
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(rule.getFunctionName());
        alertNotifyVo.setAlertDataType(rule.getAlertDataType());
        alertNotifyVo.setVoiceNotifyStatus(0);
        alertNotifyVo.setNotifyType(1);
        if(alertData.getLocationType() == 1 && alertData.getPhysicalLocationType() == 2) {
            alertNotifyVo.setIsAllConsumer(true);
        } else {
            alertNotifyVo.setIsAllConsumer(false);
        }
        webSocketServer.sendMessageToConsumer(alertNotifyVo, userIds);
    }

    /**
     * 检查上报数据是否达到阈值，未到达，删除报警数(持续周期)，直接结束
     * @param rule
     * @param data
     * @return
     */
    private boolean dataValueIsMatch(AlertRule rule, DeviceData data) {
        try {
            Object dataValue = data.getDataValue();
            Double value = Double.valueOf(dataValue.toString());
            Double ruleValue = rule.getValue();
            // 0 相等   1：前面大    -1：后面大
            if(">=".equals(rule.getOperator()) && Double.compare(value, ruleValue) < 0
                || "<".equals(rule.getOperator()) && Double.compare(value, ruleValue) >= 0) {
                log.info("上报数据属于正常范围");
                return false;
            }
        } catch (Exception e) {
            // throw new BaseException("非数值型数值没有报警规则");
            return false;
        }
        return true;
    }

    /**
     * 判断是否在生效时间内，不在：false，在：true
     * @param rule
     * @param data
     * @return
     */
    private boolean alarmTimeIsMatch(AlertRule rule, DeviceData data) {
        String[] split = rule.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(split[0]);
        LocalTime end = LocalTime.parse(split[1]);
        LocalTime alarmTime = data.getAlarmTime().toLocalTime();
        if(alarmTime.isBefore(start) || alarmTime.isAfter(end)) {
            return false;
        }
        return true;
    }
}
